package lab2;

import java.util.Iterator;
import java.util.RandomAccess;

public class MyArrayList<T> implements MyList<T>, RandomAccess {

	private int size = 0;
	private int tail = 0;

	private final int cIncreaseFactor = 1;
	private final int cIncreaseStep = 10;

	private T[] mData;

	public MyArrayList() {
		this(10);
	};

	public MyArrayList(final MyList<T> c) {
		if (c == null) {
			throw new NullPointerException();
		}

		mData = (T[]) new Object[cIncreaseFactor * c.size() + cIncreaseStep];
		if (c.size() > 0) {
			Iterator<T> iter = c.iterator();
			while (iter.hasNext()) {
				add(iter.next());
			}
		}
	}

	public MyArrayList(final int initialCapacity) {
		mData = (T[]) new Object[initialCapacity];
	}

	public void ensureCapacity(final int minCapacity) {
		if (minCapacity > mData.length) {
			T[] temp = (T[]) new Object[minCapacity];
			for (int i = 0; i < mData.length; i++) {
				temp[i] = mData[i];
			}
			mData = temp;
		}
	}
	
	public int getDataLength() {
		return mData.length;
	}

	public void trimToSize() {
		if (size < mData.length) {
			T[] temp = (T[]) new Object[size];
			for (int i = 0, j = 0; i < mData.length; i++) {
				if (mData[i] != null) {
					temp[j] = mData[i];
					j++;
				}
			}
			mData = temp;
			tail = size;
		}
	}

	public void add(final T e) {
		add(tail, e);
	}

	public void add(final int index, final T e) {
		if (index < 0 || index >= mData.length) {
			throw new MyIndexOutOfBoundsException("Index out of bounds!");
		}		

		if (tail == mData.length) {
			T[] temp = (T[]) new Object[cIncreaseFactor * mData.length + cIncreaseStep];
			for (int i = 0; i < mData.length; i++) {
				temp[i] = mData[i];
			}
			mData = temp;
		}

		for (int i = tail - 1; i >= index; i--) {
			mData[i + 1] = mData[i];
		}

		mData[index] = e;
		size++;
		tail++;
	}

	public void addAll(final T[] c) {				
		addAll(tail, c);
	}

	public void addAll(final int index, final T[] c) {
		if (index < 0 || index > mData.length) {
			throw new MyIndexOutOfBoundsException("Index out of bounds!");
		}

		if (c == null) {
			throw new NullPointerException();
		}

		if (c.length == 0) {
			return;
		}

		if (tail + c.length >= mData.length) {
			T[] temp = (T[]) new Object[cIncreaseFactor * mData.length + cIncreaseFactor * c.length];
			for (int i = 0; i < index; i++) {
				temp[i] = mData[i];
			}

			for (int i = index; i < mData.length; i++) {
				temp[i + c.length] = mData[i];
			}
			mData = temp;
		} else {
			for (int i = tail - 1; i >= index; i--) {
				mData[i + c.length] = mData[i];
			}
		}

		for (int i = 0; i < c.length; i++) {
			mData[index + i] = c[i];
		}

		size += c.length;
		tail += c.length;
	}

	public T get(final int index) {
		if (index < 0 || index >= mData.length) {
			throw new MyIndexOutOfBoundsException("Index out of bounds!");
		}
		
		if (isEmpty()) {
			throw new MyIndexOutOfBoundsException("List is empty!");
		}

		T res = mData[index];
		return res;
	}

	public T remove(final int index) {
		if (index < 0 || index >= mData.length) {
			throw new MyIndexOutOfBoundsException("Index out of bounds!");
		}
		
		if (isEmpty()) {
			throw new MyIndexOutOfBoundsException("List is empty!");
		}
		
		if (mData[index] == null) {
			return null;
		}

		T res = mData[index];
		mData[index] = null;
		size--;
		if (index == tail - 1) {
			tail--;
		}
		return res;
	}

	public void clear() {
		mData = (T[])new Object[cIncreaseStep];
		size = 0;
		tail = 0;
	}

	public boolean isEmpty() {
		return (size == 0);
	}

	public void set(final int index, final T e) {
		if (index < 0 || index >= mData.length) {
			throw new MyIndexOutOfBoundsException("Index out of bounds!");
		}

		T o = e;
		mData[index] = o;
	}

	public int indexOf(final T o) {
		if (o == null) {
			throw new NullPointerException();
		}

		if (isEmpty()) {
			return -1;
		}

		for (int i = 0; i < mData.length; i++) {
			if (mData[i].equals(o)) {
				return i;
			}
		}

		return -1;
	}

	public int size() {
		return tail;
	}

	public T[] toArray() {
		if (isEmpty()) {
			return null;
		}

		T[] res = (T[]) new Object[mData.length];
		for (int i = 0; i < mData.length; i++) {
			res[i] = mData[i];
		}

		return res;
	}

	public Iterator<T> iterator() {
		Iterator<T> it = new Iterator<T>() {

			private int currIndex = 0;

            public boolean hasNext() {
                return (currIndex < mData.length);
            }

            public T next() {
            	T res = mData[currIndex];
            	currIndex++;
                return res;
            }

            public void remove() {
                // TODO Auto-generated method stub
            }
        };
        return it;
	}

	public String toString() {
		if (isEmpty()) {
			throw new MyIndexOutOfBoundsException("List is empty!");
		}

		String res = "";

		for (int i = 0; i < mData.length - 1; i++) {
			if (mData[i] != null) {
				res += mData[i].toString() + "; ";
			} else {
				res += "null; ";
			}
		}

		if (mData[mData.length - 1] != null) {
			res += mData[mData.length - 1].toString();
		} else {
			res += "null";
		}

		return res;
	}
}
